//
// Created by Administrator on 2021/7/31.
//

#include "Geometry/Matrix4x4.h"

inline float Matrix4x4::Determinant()
{
    return data_[0][0] * (data_[1][1] * data_[2][2] - data_[1][2] * data_[2][1]) -
            data_[0][1] * (data_[1][0] * data_[2][2] - data_[1][2] * data_[2][0]) +
            data_[0][2] * (data_[1][0] * data_[2][1] - data_[1][1] * data_[2][0]);
}

inline void Matrix4x4::Trans()
{
    Matrix4x4 mat{*this};
    for(auto i = 0; i < 4; ++i)
    {
        for(auto j = 0; j < 4; ++j)
        {
            data_[j][i] = mat[i][j];
        }
    }
}

Matrix4x4 Matrix4x4::Transport()
{
    Matrix4x4 mat;
    for(auto i = 0; i < 4; ++i)
    {
        for(auto j = 0; j < 4; ++j)
        {
            mat[j][i] = data_[i][j];
        }
    }

    return mat;
}

void Matrix4x4::Invert()
{
    float det = Determinant();
    if(det < 0.0001f) return;

    float invDet = 1 / det;
    Matrix4x4 mat{*this};
    data_[0][0] = (mat[1][1] * mat[2][2] - mat[1][2] * mat[2][1]) * invDet;
    data_[0][1] = -(mat[1][0] * mat[2][2] - mat[1][2] * mat[2][0]) * invDet;
    data_[0][2] = (mat[1][0] * mat[2][1] - mat[1][1] * mat[2][0]) * invDet;
    data_[1][0] = -(mat[0][1] * mat[2][2] - mat[2][2] * mat[2][1]) * invDet;
    data_[1][1] = (mat[0][0] * mat[2][2] - mat[0][2] * mat[2][0]) * invDet;
    data_[1][2] = -(mat[0][0] * mat[2][1] - mat[0][1] * mat[2][0]) * invDet;
    data_[2][0] = (mat[0][1] * mat[1][2] - mat[0][2] * mat[2][1]) * invDet;
    data_[2][1] = -(mat[0][0] * mat[1][2] - mat[0][2] * mat[1][0]) * invDet;
    data_[2][2] = (mat[0][0] * mat[1][1] - mat[0][1] * mat[1][0]) * invDet;
}

Matrix4x4 Matrix4x4::Inverse()
{
    float det = Determinant();
    if(det < 0.0001f) return {};

    float invDet = 1 / det;
    Matrix4x4 mat;
    mat[0][0] = (data_[1][1] * data_[2][2] - data_[1][2] * data_[2][1]) * invDet;
    mat[0][1] = -(data_[1][0] * data_[2][2] - data_[1][2] * data_[2][0]) * invDet;
    mat[0][2] = (data_[1][0] * data_[2][1] - data_[1][1] * data_[2][0]) * invDet;
    mat[1][0] = -(data_[0][1] * data_[2][2] - data_[2][2] * data_[2][1]) * invDet;
    mat[1][1] = (data_[0][0] * data_[2][2] - data_[0][2] * data_[2][0]) * invDet;
    mat[1][2] = -(data_[0][0] * data_[2][1] - data_[0][1] * data_[2][0]) * invDet;
    mat[2][0] = (data_[0][1] * data_[1][2] - data_[0][2] * data_[2][1]) * invDet;
    mat[2][1] = -(data_[0][0] * data_[1][2] - data_[0][2] * data_[1][0]) * invDet;
    mat[2][2] = (data_[0][0] * data_[1][1] - data_[0][1] * data_[1][0]) * invDet;

    return mat;
}

Matrix4x4 Matrix4x4::RotateInverse()
{
    Matrix4x4 mat;
    for(auto i = 0; i < 4; ++i)
    {
        for(auto j = 0; j < 4; ++j)
        {
            mat[j][i] = data_[i][j];
        }
    }

    return mat;
}

Matrix4x4 Matrix4x4::ScaleInverse()
{
    Matrix4x4 mat;
    for(auto i = 0; i < 4; ++i)
    {
        mat[i][i] = 1 / data_[i][i];
    }

    return mat;
}

void Matrix4x4::GetData(float *data, bool transport) const
{
    for(auto i = 0; i < 4; ++i)
    {
        for(auto j = 0; j < 4; ++j)
        {
            if(transport)
            {
                data[i * 4 + j] = data_[i][j];
            }
            else
            {
                data[i * 4 + j] = data_[j][i];
            }
        }
    }
}
